class User {
  constructor(name) {
    this.name = name;
    // console.log(this);
  }

  getName() {
    return this.name;
  }

  changeSite(val) {
    this.site = val;
  }

  show() {
    return `${this.site}:${this.name}`;
  }
}
let q = new User("javascript");
q.changeSite("changeSite");
// console.log(q.show());
// console.log(q);

// console.log(new User("hahha").name); // this就是实例对象，指向 new User()

class Hd {
  show() {
    function test() {
      console.log(this);
    }
    test(); // 没有人调用这个test函数，所以test是undefined
  }
}
let hd = new Hd();
// hd.show(); //这里会输出undefined

// 静态属性，如果你只需要保存一份数据，大家共用就可以使用static关键字
class Request {
  static host = "https://www.baidu.com"; //设置为了静态属性，new出来的实例对象是访问不到的，必须用Class类去访问
  api(url) {
    return Request.host + `/${url}`;
  }
}
let ReqObj = new Request();
// console.log(ReqObj);
// console.log(ReqObj.api("article"));

//静态方法实现的原理
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  // 给实例对象使用的
  show() {
    console.log("prototype.show");
  }

  // 给Person类自己用的
  static show() {
    console.log("static.show");
  }

  static create(...args) {
    // ...args 之后就是 name, age
    return new this(...args); // 传递的是时候就是收 name, age两个参数
  }
}

let jason = Person.create("jason", 20);
// console.log(jason);

// Person.show();
let p1 = new Person();
// p1.show();

// 静态属性联系之课程管理类
const data = [
  {
    name: "js",
    price: 30,
  },
  {
    name: "php",
    price: 320,
  },
  {
    name: "java",
    price: 130,
  },
  {
    name: "golang",
    price: 930,
  },
];

class Lessons {
  constructor(data) {
    this.model = data;
  }

  get name() {
    return this.model.name;
  }

  get price() {
    return this.model.price;
  }

  static totalPeice(data) {
    return data.reudce((t, c) => {
      return t + c.price;
    }, 0);
  }

  static createBatch(data) {
    // 返回的每个对象都是实例对象了，所以每个对象都可以使用name和price
    return data.map((item) => new Lessons(item));
  }

  static maxPrice(data) {
    return data.sort((a, b) => b.price - a.price)[0];
  }
}

let lessons = Lessons.createBatch(data);
// console.log(lessons);
// console.log(Lessons.maxPrice(lessons));
// console.log(Lessons.maxPrice(lessons).price);

// 属性的保护
const protecteds = Symbol();
class Car {
  // [HOST] = "https://www.baidu.com";
  constructor() {
    this[protecteds] = {};
    this[protecteds].host = "https://www.baidu.com";
  }

  set host(url) {
    if (!/^https?:/i.test(url)) {
      throw new Error("非法网址");
    }
    this[protecteds].host = url;
  }

  get host() {
    return this[protecteds].host;
  }
}

class Byd extends Car {
  constructor(name) {
    super();
    this[protecteds].name = name;
  }
  get name() {
    return this[protecteds].name;
  }
}

let firstCar = new Byd("五菱");
// console.log(firstCar.name);
firstCar.host = "https://www.lijiahui.com";
// console.log(firstCar.host);

// public protected private

class Hobby {
  #hb1 = "play basketball";
  constructor(name) {
    this.name = name;
  }

  set hb1(val) {
    this.#hb1 = val;
  }

  get hb1() {
    return this.#hb1;
  }
}

let hobby = new Hobby("喜欢打篮球⛹️‍♀️");
// console.log(hobby.#hb1); //提示 该字段是私有字段 Private field '#hb1'
// console.log(hobby.#hb1);

/*function User1(name) {
  this.name = name;
}

function Admin1(name) {
  User1.call(this, name);
}

Admin1.prototype = Object.create(User1.prototype);
Admin1.prototype.show = function () {
  console.log("我是admin prototype中的show方法");
};
let A1 = new Admin1("我是Jason");
console.log(A1);

// A1.show(); */

// 类的方法继承原理 super()会自动调用父类的 constructor
class User1 {
  constructor(name) {
    this.name = name;
  }
  // 这样子是方法，方法是直接写在prototype上的
  // show = function () {
  //   console.log("我是父类中定义的属性方法");
  // }; // 这样子是属性，属性是供自己使用的
  show() {
    console.log("我是父类中的show方法");
  }
}

class Admin extends User1 {
  constructor(name) {
    super(); // 可以想象是用了 User1.call(this,name)
    this.name = name;
  }
}

let A1 = new Admin("你好");
// A1.show();

// super()原理分析
let hd1 = {
  name: "hd1.name",
  show() {
    console.log(this.name);
  },
};

let ljh = {
  __proto__: hd1,
  name: "ljh.name",
  show() {
    this.__proto__.show.call(this); // 用父类中的show方法, 如果你这里不传递当前的this,那么在父类中this.name永远是 hd1中的name，因为是this.__proto__调用了它，所以你这里保证我只是用父类中的方法,用call，但是不要改变我自己本身的属性
  },
};
// ljh.show();

class User2 {}
class U1 extends User2 {
  // 当你继承父类的时候，必须要调用super，并且要第一时间调用，是为了防止子类和父类的属性或者方法冲突，子类的优先级要高于父类
  constructor(name) {
    super(); // 如果就单纯调用super的话就会调用父类中的constructor方法
    this.name = name;
  }
}

let u1 = new U1("后盾人");
// console.log(u1);

class Controller {
  sum() {
    console.log("我是this", this.name);
    return this.data.reduce((a, b) => {
      return a + b.price;
    }, 0);
  }

  getBykey(key) {
    return this.data.filter((item) => item.name.includes(key));
  }
}

class Lesson extends Controller {
  constructor(data) {
    super();
    this.data = data;
    this.name = "jason";
  }

  info() {
    return {
      totlaPrice: super.sum(), // 这里相当于是 this.__proto__.sum.call(this)所以在sum中的this就是当前的Lesson这个类
      data: this.data,
    };
  }

  getBykey(key) {
    // map只需要返回name
    return super.getBykey(key).map((item) => item.name);
  }
}

let data1 = [
  { name: "js", price: 100 },
  { name: "php", price: 120 },
];

const les1 = new Lesson(data1);
// console.log(les1.info());
// console.log(les1.getBykey("js"));

// 静态基础原理分析
// prototype是生成实例对象用的，proto是本身作为对象使用的
function User3() {}
// 这些方法和属性都添加到了protot中，如果你new出来一个实力对象是用不了这些方法和属性的
User3.site = "ahah ";
User3.show = function () {
  console.log("我是User3中的show方法");
};

function Admin2() {}
Admin2.__proto__ = User3;
// Admin2.show();
// new Admin2().show();

// 使用instanceof检测对象实现，构造函数比较特殊，可以当对象又可以当函数
function User4() {}
function Admin3() {}
Admin3.prototype = Object.create(User4.prototype); // 开辟一个新的对象，对象名字就是Admin3.prototpe 将原型对象指向 User4.prototype这个
// console.log(Admin3.prototype.__proto__.__proto__.__proto__);
// console.log(Admin3.prototype.__proto__ == User4.prototype);
let U2 = new User4();
// console.log(U2 instanceof Object); // 是不是通过Object来实现的 就这样理解就可以了

let a = {};
let b = {
  __proto__: a,
};
let c = {
  __proto__: b,
};

// console.log(a.isPrototypeOf(b));
// console.log(a.__proto__.isPrototypeOf(c));

function Arr1(...args) {
  args.forEach((item) => this.push(item));
  this.first = function () {
    return this[0];
  };

  this.max = function () {
    return this.sort((a, b) => b - a)[0];
  };
}
Arr1.prototype = Object.create(Array.prototype);
let hd2 = new Arr1(99, 12, 39, 10);
// console.log(hd2);
// console.log(hd2.first());

class Arr2 extends Array {
  constructor(...args) {
    super(...args);
    // console.log(this); // this就是当前这个对象，它继承了Array，可以用数组的方法
  }
  first() {
    return this[0];
  }

  add(item) {
    this.push(item);
  }

  remove(val) {
    let pos = this.findIndex((item) => item == val);
    this.splice(pos, 1);
  }
}

let hd3 = new Arr2(1, 3, 24);
hd3.remove(3);
// console.log(hd3);

// mixin混合模式使用技巧
let Tool = {
  max(key) {
    return this.data.sort((a, b) => b[key] - a[key])[0];
  },
  cot() {
    return this.data.reduce((a, b) => {
      return a + b.price;
    }, 0);
  },
};

let Count = {};

class Lessons1 {
  constructor(data) {
    this.lessons = data;
  }

  get data() {
    return this.lessons;
  }
}

Object.assign(Object.prototype, Tool);

let data2 = [
  { name: "java", price: 100 },
  { name: "php", price: 200 },
  { name: "javascript", price: 300 },
];

let hd4 = new Lessons1(data2);
console.log(hd4.max("java"));
console.log(hd4.cot());
